我們講完了一般的 unit test,是時候來測試一些 Android UI 相關的程式了。
基本上 UI test 會做以下這幾件事情:
我們今天會以 Android 官方推薦的 espresso 為教學,馬上就開始吧!
首先一樣是加上 dependency:
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
還記得我們 /src 下有 /main、/test 跟 /androidTest 三個資料夾嗎?
androidTestImplementation
就是針對 /androidTest 的 dependency 設定。
另外在 android.defaultConfig 必須加上以下設定:
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
以下是一個完整的 build.gradle
範例檔案:
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.androidtestsample"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
我們一開始所說的三件事情,在 espresso 中也有相對應的函示:
ViewMatcher
,withId
、withText
等找出 View
的方法。ViewInteraction
,onView
建立 ViewInteraction
然後就可以呼叫 perform
來進行像是 click
、typeText
等 View
的操作。ViewAssertion
,matches
建立我們的 ViewAssertion
,然後在 ViewInteraction
下使用 check
函式來連結我們的 ViewAssertion
。如果大家還是很模糊的話,讓我們來看個例子,假設我們在程式端有以下程式碼:
val account = findViewById<TextView>(R.id.account)
val submit = findViewById<Button>(R.id.submit)
submit.isEnabled = false
account.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (s != null) {
submit.isEnabled = s.isNotEmpty()
} else {
submit.isEnabled = false
}
}
})
如果我們想要測試當 account
輸入的時候,submit
的狀態有沒有正常跟著改變就可以這樣寫:
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
@Rule
@JvmField
val activityRule = ActivityTestRule(LoginActivity::class.java)
@Test
fun checkSubmitState() {
// submit should be disable at first
onView(withId(R.id.submit))
.check(matches(not(isEnabled())))
// type Jintin
onView(withId(R.id.account))
.perform(typeText("Jintin"))
// submit should be enable now
onView(withId(R.id.submit))
.check(matches(isEnabled()))
// clear the text
onView(withId(R.id.account))
.perform(clearText())
// submit should be disable now
onView(withId(R.id.submit))
.check(matches(not(isEnabled())))
}
}
跟 unit test 一樣,不論是函示或 class 前面都有個小綠箭頭可以執行我們的測試。
onView
有點像 espresso 的起手式,裡面的 withId
可以幫我們找到我們關注的 View
。
接下來藉由 perform
來執行一或多個動作,比如說是 typeText
、click
或 clearText
。
第三步就是藉由 check
來執行 ViewAssertion
的檢核,matches
會將 ViewMatcher
包裝成 ViewAssertion
,常見的 ViewMatcher
有 isEnabled
、isDisplayed
等,狀態基本上都是正面表列,有需要的話也可以跟 not
這種可以轉換成反面的 matcher 連起來使用(就像我們的 matches(not(isEnabled()))
)。
是不是跟測試 3A 很像呢,以上就是今天的內容了,感謝大家!
也歡迎參考 Android 十全大補的測試三部曲的其他文章:
參考資料:
https://developer.android.com/training/testing/espresso
Android 十全大補已經正式出書上架囉!
有興趣的讀者歡迎參考:
https://www.tenlong.com.tw/products/9789864345786